package com.dap.core.container;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.CountDownLatch;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;

import com.alibaba.dubbo.config.ProtocolConfig;
import com.dap.core.component.ComponentShutdownHook;
import com.dap.core.config.BootstrapConfigUtil;
import com.dap.core.context.ContextUtils;
import com.dap.core.context.DapContext;
import com.dap.core.utils.StringUtils;

public class Bootstrapper {
    private static final Logger LOGGER = LoggerFactory.getLogger(Bootstrapper.class);
    private static final String SPRING_CONTEXT_MAIN = "spring.context.main";
    private CountDownLatch latch;
    private AbstractApplicationContext dapContext = null;
    private AbstractApplicationContext appContext = null;



    private ShutdownHookThread shutdownHookThread = null;
    private class ShutdownHookThread extends Thread {
        @Override
        public void run() {
            shutdown();
        }
    }

    public static void main(String[] args) {
        Bootstrapper bootstrapper = new Bootstrapper();
        try {
            bootstrapper.init();
            bootstrapper.start();
            bootstrapper.blocking();// 线程阻塞
        } catch (Exception e) {
            LOGGER.error("", e);
            bootstrapper.shutdown();
            System.exit(1);
        }
    }



    public void init() {
        printAppInfo();


        List<String> componentsList = new ArrayList<>();
        try {
            // components为组件加载选项, 如: "base|governance"
            String components = BootstrapConfigUtil.getProperty(BootstrapConfigUtil.LOAD_COMPONENTS);
            // 不配置默认全部加载
            if (StringUtils.isEmpty(components)) {
                componentsList.add(DapContext.DAP_ALL_COMPONENT_PATH);
            } else {
                String[] componentsArr = (components).split("\\|");
                for (int i = 0; i < componentsArr.length; i++) {
                    if (!StringUtils.isEmpty(componentsArr[i])) {
                        componentsList.add(DapContext.DAP_COMPONENT_PATH_PREFIX + componentsArr[i] + DapContext.DAP_COMPONENT_PATH_SUFFIX);
                    }
                }
            }
        } catch (RuntimeException e) {
            LOGGER.error("平台组件配置文件加载失败!");
            throw e;
        }
        try {
            String[] componentsArr = new String[componentsList.size()];
            componentsList.toArray(componentsArr);
            dapContext = new DapXmlApplicationContext(componentsArr);
            ContextUtils.setSpringDapContext(dapContext);
        } catch (RuntimeException e) {
            LOGGER.error("平台组件Spring上下文加载失败!");
            throw e;
        }


        try {
            String springContextMain = BootstrapConfigUtil.getProperty(SPRING_CONTEXT_MAIN);
            if (StringUtils.isEmpty(springContextMain)) {
                springContextMain = DapContext.DEFAULT_APP_SPRING_CONFIG;
            }
            appContext = new DapXmlApplicationContext(dapContext, new String[] { springContextMain });
            ContextUtils.setSpringAppContext(appContext);
        } catch (RuntimeException e) {
            LOGGER.error("应用Spring上下文加载失败!");
            throw e;
        }
    }


    public void start() {
        if (appContext == null) {
            LOGGER.error("容器启动失败, 未初始化!");
        }
        try {
            appContext.start();
        } catch (RuntimeException e) {
            LOGGER.error("容器启动失败!");
            throw e;
        }

        shutdownHookThread = new ShutdownHookThread();
        Runtime.getRuntime().addShutdownHook(shutdownHookThread);

        LOGGER.info(" ********************************************************");
        LOGGER.info(" *** Distributed Application Platform is started! ***");
        LOGGER.info(" ********************************************************");
        ContextUtils.setApplicationStarted(true);
        if (LOGGER.isDebugEnabled()) {
            printBeans(appContext);
        }
    }


    public void blocking() {
        this.latch = new CountDownLatch(1);
        try {
            this.latch.await();
        } catch (InterruptedException e) {
            LOGGER.error("创建平台后台运行daemon失败", e);
        }
    }


    private volatile boolean hasHandleShutdown = false;
    public void shutdown() {
        if (hasHandleShutdown) {
            return;
        } else {
            hasHandleShutdown = true;
        }

        LOGGER.error(" *** 开始关闭 ...");

        if (this.latch != null) {
            this.latch.countDown();
        }

        // dubbo shutdown
        ProtocolConfig.destroyAll();

        if (appContext != null && appContext.isActive()) {
            appContext.close();
        }
        if (dapContext != null && dapContext.isActive()) {
            Iterator<Entry<String, ComponentShutdownHook>> iter =
                    dapContext.getBeansOfType(ComponentShutdownHook.class).entrySet().iterator();
            while (iter.hasNext()) {
                Entry<String, ComponentShutdownHook> entry = iter.next();
                LOGGER.info("Destroy {} ", entry.getKey());
                try {
                    entry.getValue().register();
                } catch (Exception e) {
                    LOGGER.warn("Destroy {} cause an Exception !", entry.getKey(), e);
                }
            }

            dapContext.close();
        }

        LOGGER.info(" ********************************************************");
        LOGGER.info(" *** Distributed Application Platform is shutdown! ***");
        LOGGER.info(" ********************************************************");
    }



    protected static void printBeans(ApplicationContext context) {
        String[] beanNames = context.getBeanDefinitionNames();
        Arrays.sort(beanNames);
        StringBuilder sb = new StringBuilder();
        for (String beanName : beanNames) {
            sb.append("\t").append(beanName).append("\r\n");
        }
        LOGGER.debug("Context included bean names:" + sb.toString());
    }

    private static void printAppInfo() {
        LOGGER.info(" ********************************************************");
        Object[] appInfo = { BootstrapConfigUtil.getProperty(BootstrapConfigUtil.APP_TENANTID),
                             BootstrapConfigUtil.getProperty(BootstrapConfigUtil.APP_NAME),
                             BootstrapConfigUtil.getProperty(BootstrapConfigUtil.APP_ENV),
                             BootstrapConfigUtil.getProperty(BootstrapConfigUtil.APP_VERSION) };

        LOGGER.info("Application Info:TenantId:[{}],AppName[{}],Env[{}],Version[{}]", appInfo);
        LOGGER.info(" ********************************************************");
    }
}
